///////////////////////////////////////////////////////////////////////////////////
//
// Writer  : Dong-moon Lim, Dept. of Messenger, SK Communications, Korea
//
// Email   : dmlim@nate.com
//
// Created : 2008-06-05 11:31:40
//
// Description
//		This module's major function is encoding ASN.1 to DER format
//		CAPICOM uses "CAPICOM.EncryptedData" in Microsoft own format,
//		compatible with PKCS7 "Enveloped Data". 
//
// For more information 
//		http://msdn.microsoft.com/en-us/library/bb540802(VS.85).aspx
//
///////////////////////////////////////////////////////////////////////////////////

#ifndef __ASN1_H__
#define __ASN1_H__

#if WIN32 && _MSC_VER < 1300 
#define __FUNCTION__ "Unknown"
#pragma warning( disable : 4786 )
#endif 

#ifdef WIN32
#include <windows.h>
#endif

#include <vector>
#include <list>
#include <map>
#include <set>
#include <ncrypt_types.h>

#define ASN_PARSE_DECL BYTE* asn_start, unsigned int asn_depth

#define ASN_PRINT_DECL unsigned int w_offset, unsigned int w_depth, unsigned int w_head, unsigned int w_body, FILE* fp
#define ASN_PRINT_CALL w_offset, w_depth, w_head, w_body, fp

#define ASN_OBJ_IMPLS \
	protected: \
		virtual unsigned int GetContentLength(); \
		virtual BYTE* EncodeContent(BYTE* buf); \
		virtual BYTE* DecodeContent(ASN_PARSE_DECL, BYTE* buf, unsigned int size, std::list<std::string> *e); \
		virtual void PrintContent(ASN_PRINT_DECL);

namespace ASN1
{
	enum TYPE_ENUM
	{
		UNIVERSAL		= 0x00,

		BOOLEAN			= 0x01,
		INTEGER			= 0x02,
		BIT_STRING		= 0x03,
		OCTET_STRING	= 0x04,
		NULL_OBJECT		= 0x05,
		OBJECT_ID		= 0x06,
		UTF8_STRING		= 0x0C,
		PRNTBL_STRING	= 0x13,
		TELETEX_STRING	= 0x14,
		IA5_STRING		= 0x16,
		UTCTIME			= 0x17,
		BMP_STRING		= 0x1E,
		SEQUENCE		= 0x30,
		SET				= 0x31,

		APPLICATON		= 0x40,

		CONTEXT			= 0x80,

		CONTEXT_CONST	= 0xa0,

		PRIVATE			= 0xc0,
	};

	/////////////////////////////////////////////////////////////////////

	class Object
	{
	protected:
		BYTE	m_type;
		const char*		m_name;
		Object(BYTE t);

		unsigned int	m_offset;
		unsigned int	m_depth;
		unsigned int	m_head_len;
		unsigned int	m_body_len;
		BYTE*	m_raw_byte;

		static std::map<BYTE, const char*> gTypeNameMap;
		static std::set<BYTE> gKnownUniversalTypeSet;
		static Object* Decode(ASN_PARSE_DECL, BYTE* buf, /*in out*/ unsigned int *size, std::list<std::string> *e);

		virtual unsigned int GetContentLength() = 0;
		virtual BYTE* EncodeContent(BYTE* buf) = 0;
		virtual BYTE* DecodeContent(ASN_PARSE_DECL, BYTE* buf, unsigned int size, std::list<std::string> *e) = 0;
		virtual void PrintContent(ASN_PRINT_DECL) = 0;
		virtual void PrintRawBytes(ASN_PRINT_DECL);

	public:

		virtual ~Object() {};
		static int Init();
		static Object* Decode(BYTE* buf, /*in out*/ unsigned int *size, std::list<std::string> *e);
		
		virtual BYTE* EncodeTypeAndLength(BYTE* buf);
		virtual unsigned int GetEncodedLength();
		virtual BYTE* Encode(BYTE* buf);
		virtual void Print(FILE* fp);
		virtual void Print(ASN_PRINT_DECL);
		virtual void GetMaxLength(unsigned int *offset, unsigned int *depth, unsigned int *head_len, unsigned int *body_len);

		BYTE GetType();
		BYTE GetTagNumber();
		const char* GetTypeName();

		static bool isUniversal(BYTE type);
		static bool isApplication(BYTE type);
		static bool isContextDefined(BYTE type);
		static bool isPrivate(BYTE type);
		static bool isPrimitive(BYTE type);
		static bool isConstructed(BYTE type);
		static bool isKnownUniversal(BYTE type);

		bool isUniversal();
		bool isApplication();
		bool isContextDefined();
		bool isPrivate();
		bool isPrimitive();
		bool isConstructed();
		bool isKnownUniversal();
	};

	/////////////////////////////////////////////////////////////////////

	class Boolean : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		bool m_value;

	public:
		Boolean();
		Boolean(bool v);
		virtual ~Boolean();

		bool GetValue();
		void SetValue(bool v);
	};

	/////////////////////////////////////////////////////////////////////

	class Integer : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		int m_value;

	public:
		Integer();
		Integer(int v);
		virtual ~Integer();

		int GetValue();
		void SetValue(int v);
	};

	/////////////////////////////////////////////////////////////////////

	class NullObject : public Object
	{
		ASN_OBJ_IMPLS;

	public:
		NullObject();
		virtual ~NullObject();
	};

	/////////////////////////////////////////////////////////////////////

	class BitString : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		BYTE* m_data;
		unsigned int m_bit_size;
		bool m_iscopy;
		void Clear();

	public:
		BitString();
		BitString(BYTE* data, unsigned int bit_size, bool iscopy = false);
		virtual ~BitString();

		void SetValue(BYTE* data, unsigned int bit_size, bool iscopy = false);
		const BYTE* GetData();
		unsigned int GetBitSize();
	};

	/////////////////////////////////////////////////////////////////////

	class BinaryObject : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		BYTE* m_data;
		unsigned int m_size;
		bool m_iscopy;

		void Clear();

	public:
		BinaryObject(BYTE type);
		BinaryObject(BYTE type, BYTE* data, unsigned int size, bool iscopy = false);
		virtual ~BinaryObject();

		void SetValue(BYTE* data, unsigned int size, bool iscopy = false);
		const BYTE* GetData();
		unsigned int GetSize();
	};

	/////////////////////////////////////////////////////////////////////

	class UnknownObject : public BinaryObject
	{
	protected:
		virtual void PrintContent(ASN_PRINT_DECL);

	public:
		UnknownObject(BYTE type);
		UnknownObject(BYTE type, BYTE* data, unsigned int size, bool iscopy = false);
		virtual ~UnknownObject();
	};

	/////////////////////////////////////////////////////////////////////

	class OctetString : public BinaryObject
	{
	public:
		OctetString();
		OctetString(BYTE* data, unsigned int size, bool iscopy = false);
		virtual ~OctetString();
	};

	/////////////////////////////////////////////////////////////////////

	class String : public Object
	{
	protected:
		WCHAR* m_unicode;
		char* m_utf8;
		void Convert();

	public:
		String(BYTE type);
		String(BYTE type, const char* str);
		String(BYTE type, const WCHAR* str);
		virtual ~String();

		void SetString(const char* str, int len = -1);
		void SetString(const WCHAR* str, int len = -1);
		const char* GetUTF8String();
		const WCHAR* GetUnicodeString();
	};

	/////////////////////////////////////////////////////////////////////

	class MultibyteString : public String
	{
		ASN_OBJ_IMPLS;

	public:
		MultibyteString(BYTE type);
		MultibyteString(BYTE type, const char* str);
		MultibyteString(BYTE type, const WCHAR* str);
		virtual ~MultibyteString();
	};

	/////////////////////////////////////////////////////////////////////

	class UTF8String : public MultibyteString
	{
	public:
		UTF8String();
		UTF8String(const char* str);
		UTF8String(const WCHAR* str);
		virtual ~UTF8String();
	};

	/////////////////////////////////////////////////////////////////////

	class IA5String : public MultibyteString
	{
	public:
		IA5String();
		IA5String(const char* str);
		IA5String(const WCHAR* str);
		virtual ~IA5String();
	};

	/////////////////////////////////////////////////////////////////////

	class PrintableString : public MultibyteString
	{
	public:
		PrintableString();
		PrintableString(const char* str);
		PrintableString(const WCHAR* str);
		virtual ~PrintableString();
	};

	/////////////////////////////////////////////////////////////////////

	class TeletexString : public MultibyteString
	{
	public:
		TeletexString();
		TeletexString(const char* str);
		TeletexString(const WCHAR* str);
		virtual ~TeletexString();
	};

	/////////////////////////////////////////////////////////////////////

	class UTCTime : public MultibyteString
	{
	public:
		UTCTime();
		UTCTime(const char* str);
		UTCTime(const WCHAR* str);
		virtual ~UTCTime();
	};

	/////////////////////////////////////////////////////////////////////

	class BMPString : public String
	{
		ASN_OBJ_IMPLS;

	public:
		BMPString();
		BMPString(const char* str);
		BMPString(const WCHAR* str);
		virtual ~BMPString();
	};

	/////////////////////////////////////////////////////////////////////

	class ObjectID : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		char* m_oid;
		std::vector<unsigned int> m_idList;

		void Clear();
		void ParseID();
		void GenerateID();
		unsigned int GetFieldLength(unsigned int val);
		BYTE* EncodeField(BYTE* buf, unsigned int val1, unsigned int val2);
		BYTE* EncodeField(BYTE* buf, unsigned int val);
		BYTE* DecodeField(BYTE* buf, unsigned int *val1, unsigned int *val2);
		BYTE* DecodeField(BYTE* buf, int len, unsigned int *val);

	public:
		ObjectID();
		ObjectID(const char* oid);
		ObjectID(const WCHAR* oid);
		virtual ~ObjectID();

		void SetValue(const char* oid);
		void SetValue(const WCHAR* oid);
		const char* GetValue();
	};

	/////////////////////////////////////////////////////////////////////

	class Constructed : public Object
	{
		ASN_OBJ_IMPLS;

	protected:
		std::vector<std::pair<Object*, bool> > m_objList;
		void ClearAll();

	public:
		Constructed(BYTE type);
		virtual ~Constructed();

		unsigned int GetSize();
		void AddObject(Object* obj, bool destroy = false);
		const Object* GetObjectAt(unsigned int idx);
		virtual void GetMaxLength(unsigned int *offset, unsigned int *depth, unsigned int *head_len, unsigned int *body_len);
	};

	/////////////////////////////////////////////////////////////////////

	class Sequence : public Constructed
	{
	public:
		Sequence();
		virtual ~Sequence();
	};

	/////////////////////////////////////////////////////////////////////

	class Set : public Constructed
	{
	public:
		Set();
		virtual ~Set();
	};

	/////////////////////////////////////////////////////////////////////

	class ContextDefinedObject : public Constructed
	{
	public:
		ContextDefinedObject(BYTE type = CONTEXT_CONST, Object* obj = 0, bool destroy = false);
		ContextDefinedObject(Object* obj, bool destroy = false);
		virtual ~ContextDefinedObject();

		void SetValue(Object* obj, bool destroy = false);
	};

	/////////////////////////////////////////////////////////////////////

	class NamedObject : public Sequence
	{
	protected:
		ObjectID *m_oid;
		ContextDefinedObject *m_cont;

		void SetList();

	public:
		NamedObject();
		NamedObject(const char* oid, Object* obj);
		NamedObject(const WCHAR* oid, Object* obj);
		virtual ~NamedObject();

		void SetValue(const char* oid, Object* obj);
		void SetValue(const WCHAR* oid, Object* obj);
	};
}

#endif // __ASN1_H__
